package mylittleinvestigations.HeightMaps;

import java.io.IOException;
import mylittleinvestigations.CaseInformation.CaseXmlReader;
import mylittleinvestigations.CaseInformation.CaseXmlWriter;
import mylittleinvestigations.Line;
import mylittleinvestigations.Vector2;

public class ParabolicHeightMap extends HeightMap
{
    private HeightLine heightLine1;
    private HeightLine heightLine2;
    private HeightLine heightLine3;
    
    public ParabolicHeightMap() { }
    
    public void SetHeightLine1(Vector2 pointInLine, Vector2 directionVector, int heightAtLine)
    {
        this.heightLine1 = new HeightLine(pointInLine, directionVector, heightAtLine);
    }
    
    public void SetHeightLine2(Vector2 pointInLine, Vector2 directionVector, int heightAtLine)
    {
        this.heightLine2 = new HeightLine(pointInLine, directionVector, heightAtLine);
    }
    
    public void SetHeightLine3(Vector2 pointInLine, Vector2 directionVector, int heightAtLine)
    {
        this.heightLine3 = new HeightLine(pointInLine, directionVector, heightAtLine);
    }
    
    @Override
    public int GetHeightAtPoint(Vector2 point)
    {
        Line characterDirectionLine = new Line(point, this.directionVector);
        
        Vector2 heightLine1IntersectionPoint = characterDirectionLine.GetIntersectionPointWith(this.heightLine1);
        Vector2 heightLine2IntersectionPoint = characterDirectionLine.GetIntersectionPointWith(this.heightLine2);
        Vector2 heightLine3IntersectionPoint = characterDirectionLine.GetIntersectionPointWith(this.heightLine3);
        
        double distanceToHeightLine1 = heightLine1IntersectionPoint.subtract(point).length();
        double distanceToHeightLine2 = heightLine2IntersectionPoint.subtract(point).length();
        double distanceToHeightLine3 = heightLine3IntersectionPoint.subtract(point).length();
        
        boolean inFirstHalf =
                distanceToHeightLine3 > distanceToHeightLine1 &&
                distanceToHeightLine3 > distanceToHeightLine2;
        
        if (inFirstHalf)
        {
            double normalizedDistance = distanceToHeightLine1 / (distanceToHeightLine1 + distanceToHeightLine2);
            double weight = (1 - normalizedDistance) * (1 - normalizedDistance);
            
            return (int)Math.round(this.heightLine1.getHeightAtLine() * weight + this.heightLine2.getHeightAtLine() * (1 - weight));
        }
        else
        {
            double normalizedDistance = distanceToHeightLine3 / (distanceToHeightLine3 + distanceToHeightLine2);
            double weight = (1 - normalizedDistance) * (1 - normalizedDistance);
            
            return (int)Math.round(this.heightLine3.getHeightAtLine() * weight + this.heightLine2.getHeightAtLine() * (1 - weight));
        }
    }

    @Override
    protected int GetHighestHeight()
    {
        return Math.max(this.heightLine1.getHeightAtLine(), Math.max(this.heightLine2.getHeightAtLine(), this.heightLine3.getHeightAtLine()));
    }

    @Override
    public void SaveToXml(CaseXmlWriter writer) throws IOException
    {
        writer.StartElement("ParabolicHeightMap");
        
        this.SaveToXmlCore(writer);
        
        writer.StartElement("HeightLine1");
        this.heightLine1.SaveToXml(writer);
        writer.EndElement();
        
        writer.StartElement("HeightLine2");
        this.heightLine2.SaveToXml(writer);
        writer.EndElement();
        
        writer.StartElement("HeightLine3");
        this.heightLine3.SaveToXml(writer);
        writer.EndElement();
        
        writer.EndElement();
    }

    public ParabolicHeightMap(CaseXmlReader reader)
    {
        reader.StartElement("ParabolicHeightMap");
        
        this.LoadFromXmlCore(reader);
        
        reader.StartElement("HeightLine1");
        this.heightLine1 = new HeightLine(reader);
        reader.EndElement();
        
        reader.StartElement("HeightLine2");
        this.heightLine2 = new HeightLine(reader);
        reader.EndElement();
        
        reader.StartElement("HeightLine3");
        this.heightLine3 = new HeightLine(reader);
        reader.EndElement();
        
        reader.EndElement();
    }
    
    private class HeightLine extends Line
    {
        private int heightAtLine;
        
        public int getHeightAtLine() { return this.heightAtLine; }
        public void setHeightAtLine(int heightAtLine) { this.heightAtLine = heightAtLine; }
        
        public HeightLine(Vector2 pointInLine, Vector2 directionVector, int heightAtLine)
        {
            super(pointInLine, directionVector);
            this.heightAtLine = heightAtLine;
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("HeightLine");
            this.SaveToXmlCore(writer);
            writer.WriteIntElement("HeightAtLine", this.heightAtLine);
            writer.EndElement();
        }
        
        public HeightLine(CaseXmlReader reader)
        {
            reader.StartElement("HeightLine");
            this.LoadFromXmlCore(reader);
            this.heightAtLine = reader.ReadIntElement("HeightAtLine");
            reader.EndElement();
        }
    }
}
